#include "Vector3.h"

Vector3::Vector3()
{
	_x = 0.0;
	_y = 0.0;
	_z = 0.0;
	_w = 1.0;
}
Vector3::Vector3( const Vector3& vec )
{
	_x = vec.getX();
	_y = vec.getY();
	_z = vec.getZ();
	_w = vec.getW();
}

Vector3::Vector3( const Vector2& vec )
{
	_x = vec.getX();
	_y = vec.getY();
	_z = 0.0;
	_w = vec.getW();
}

Vector3::Vector3( float x, float y, float z)
{
	_x = x;
	_y = y;
	_z = z;
	_w = 1.0;
}
Vector3::Vector3( float x, float y, float z, float w )
{
	_x = x;
	_y = y;
	_z = z;
	_w = w;
}

//Getter/Setter
float Vector3::getX() const
{
	return this->_x;
}
float Vector3::getY() const
{
	return this->_y;
}
float Vector3::getZ() const
{
	return this->_z;
}
float Vector3::getW() const
{
	return this->_w;
}
void Vector3::setX(float x)
{
	this->_x = x;
}
void Vector3::setY(float y)
{
	this->_y = y;
}
void Vector3::setZ(float z)
{
	this->_z = z;
}
void Vector3::setW(float w)
{
	this->_w = w;
}

float Vector3::getAbsolute() const
{
	return sqrt( (_x*_x) + (_y*_y) + (_z*_z) );
}

float Vector3::getSquare() const
{
	return (_x*_x) + (_y*_y) + (_z*_z);
}

float Vector3::getAngle( const Vector3& vec2) const
{
	return acos( ( (*this)*vec2 ) / ( this->getAbsolute() * vec2.getAbsolute() ) );
}

void Vector3::setLength( float length )
{
	float len = this->_x*this->_x + this->_y*this->_y + this->_z*this->_z;
	if(length >= 0.0 && fabs(len - length * length) < (GeoMath::EPS * GeoMath::EPS))
	return;

	if(len <= (GeoMath::EPS * GeoMath::EPS))
		return;

	float val = length;
	val = val / sqrt(len);
	this->_x *= val;
	this->_y *= val;
	this->_z *= val;
}

Vector3 Vector3::operator + (const Vector3& vec2) const
{
	Vector3 vec(this->_x + vec2._x,this->_y + vec2._y,this->_z + vec2._z);
	return vec;
};

void Vector3::operator += (const Vector3& vec2)
{
	this->_x += vec2.getX();
	this->_y += vec2.getY();
	this->_z += vec2.getZ();
};

Vector3 Vector3::operator - (const Vector3& vec2) const
{
	Vector3 vec(this->_x - vec2._x,this->_y - vec2._y,this->_z - vec2._z);
	return vec;
};

void Vector3::operator -= (const Vector3& vec2)
{
	this->_x -= vec2.getX();
	this->_y -= vec2.getY();
	this->_z -= vec2.getZ();
};

float Vector3::operator * (const Vector3& vec2) const
{
	return ( this->_x * vec2._x ) + (this->_y * vec2._y) + (this->_z * vec2._z);
};	

Vector3 Vector3::operator * (float scalar) const
{
	Vector3 tmp( this->_x * scalar, this->_y * scalar, this->_z * scalar );
	return tmp;
};	

void Vector3::operator *= (float scalar)
{
	this->_x *= scalar;
	this->_y *= scalar;
	this->_z *= scalar;
};

Vector3 Vector3::operator / (float scalar) const
{
	Vector3 null;
	if( fabs(scalar) < GeoMath::EPS )
		 return null;
	Vector3 tmp( this->_x / scalar, this->_y / scalar, this->_z / scalar );
	return tmp;
};	

void Vector3::operator /= (float scalar)
{
	if( fabs(scalar) < GeoMath::EPS )
		 return;
	this->_x /= scalar;
	this->_y /= scalar;
	this->_z /= scalar;
};

// vector produkt steht orthogonal zu den beiden vektoren!
Vector3 Vector3::vecProduct(const Vector3& vec2) const
{
	Vector3 tmp(
						(this->_y*vec2._z) - (this->_z*vec2._y),
						(this->_z*vec2._x) - (this->_x*vec2._z),
						(this->_x*vec2._y) - (this->_y*vec2._x)
					);
	return tmp;
};	

bool Vector3::isNullVector() const
{
	return ( ( fabs( _x ) < GeoMath::EPS ) && ( fabs( _y ) < GeoMath::EPS ) && ( fabs( _z ) < GeoMath::EPS ) );
}

Vector3 Vector3::getNormal() const
{
	if( isNullVector() )
		return *this;

	float abs = getAbsolute();
	Vector3 tmp( _x / abs, _y / abs, _z / abs );

	return tmp;
}

void Vector3::normalize()
{
	if( isNullVector() )
		return;

	float abs = getAbsolute();
	_x /= abs;
	_y /= abs;	
	_z /= abs;
}

float Vector3::componentsSum() const
{
	return _x + _y + _z;
}

Vector3 Vector3::projectionOn( const Vector3& vec2 ) const
{
	if( vec2.isNullVector() )
		return Vector3();
	
	return vec2 * ( ( (*this) * vec2) /  vec2.getSquare() );	
}


ostream & operator<< (ostream &o, const Vector3 &vec)
{
	float x = vec.getX(),y = vec.getY(),z = vec.getZ(),w = vec.getW();

	if( fabs(vec.getX()) < GeoMath::EPS )
		x = 0.0;

	if( fabs(vec.getY()) < GeoMath::EPS )
		y = 0.0;

	if( fabs(vec.getZ()) < GeoMath::EPS )
		z = 0.0;

	if( fabs(vec.getW()) < GeoMath::EPS )
		w = 0.0;

	o << "(" << x << "," << y << "," << z << ":" << w << ":)";
	return o;
};


